Va chạm và Vật lý (Collision Detection & Physics)
Phát hiện va chạm và phản hồi bằng vật lý trong JavaScript. Kiểm tra sự đè lên nhau giữa các hình khối, áp dụng hitbox và tính toán lại vận tốc.
Kiểm tra va chạm giữa các hình chữ nhật (AABB Collision)
Mẫu va chạm AABB (Axis-Aligned Bounding Box) kiểm tra va chạm giữa các hình chữ nhật song song với trục tọa độ:
function rectIntersect(x1, y1, w1, h1, x2, y2, w2, h2) {
// Kiểm tra nếu 2 hình chữ nhật KHÔNG đè lên nhau
if (x2 > w1 + x1 || x1 > w2 + x2 || y2 > h1 + y1 || y1 > h2 + y2){
return false;
}
return true;
}
Va chạm giữa các hình tròn (Circle Collision)
Kiểm tra khoảng cách giữa tâm 2 hình tròn so với tổng bán kính của chúng:
function circleIntersect(x1, y1, r1, x2, y2, r2) {
// Tính khoảng cách giữa 2 tâm
let squareDistance = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
// So sánh với bình phương tổng bán kính
return squareDistance <= (r1 + r2) * (r1 + r2);
}
Phản hồi va chạm vật lý (Physics Collision Response)
Khi hai đối tượng hình tròn va chạm, ta tính toán lại vận tốc của chúng dựa trên định luật bảo toàn động lượng và độ đàn hồi (restitution):
function detectCollisions() {
let obj1;
let obj2;
for (let i = 0; i < gameObjects.length; i++) {
for (let j = i + 1; j < gameObjects.length; j++) {
obj1 = gameObjects[i];
obj2 = gameObjects[j];
if (circleIntersect(obj1.x, obj1.y, obj1.radius, obj2.x, obj2.y, obj2.radius)) {
// Xử lý nảy lại (bounce)
let vCollision = {x: obj2.x - obj1.x, y: obj2.y - obj1.y};
let distance = Math.sqrt((obj2.x-obj1.x)*(obj2.x-obj1.x) + (obj2.y-obj1.y)*(obj2.y-obj1.y));
let vCollisionNorm = {x: vCollision.x / distance, y: vCollision.y / distance};
let vRelativeVelocity = {x: obj1.vx - obj2.vx, y: obj1.vy - obj2.vy};
let speed = vRelativeVelocity.x * vCollisionNorm.x + vRelativeVelocity.y * vCollisionNorm.y;
if (speed < 0) break;
let impulse = 2 * speed / (obj1.mass + obj2.mass);
obj1.vx -= (impulse * obj2.mass * vCollisionNorm.x);
obj1.vy -= (impulse * obj2.mass * vCollisionNorm.y);
obj2.vx += (impulse * obj1.mass * vCollisionNorm.x);
obj2.vy += (impulse * obj1.mass * vCollisionNorm.y);
}
}
}
}